package jass.patches;
import jass.engine.*;
import jass.generators.*;

/** Allpass with delay M  Used in Moorers reverb. See e.g.
    @book{Steiglitz96,
	title		= {A Digital Signal Processing Primer with
                          Applications to Digital Audio and Computer Music},
	author          =  {Ken Steiglitz},
	publisher	= {Addison-Wesley},
	address		= {New York},
	year		= {1996},
    pages = {290--295}}
    y = H(z) x
    H(z) = (z^{-m} + a)/(1 + a*z^{-m}))
    Must have delay greater than the bufferSize.
    BUGS: Does not support removal of source.
    @author Kees van den Doel (kvdoel@cs.ubc.ca)
*/
public class AllPass extends InOut {

    protected float srate;
    protected float del; // delay in seonds
    protected float a=0; // Filter parameter
    protected Mixer mixer;
    protected Delay delay1;
    protected Delay delay2;

    /** Create. For derived classes.
        @param bufferSize Buffer size used for real-time rendering.
     */
    public AllPass(int bufferSize) {
        super(bufferSize);
    }

    /** Create. For derived classes.
        @param bufferSize Buffer size used for real-time rendering.
        @param srate sampling rate in Hz
     */
    public AllPass(int bufferSize,float srate) {
        super(bufferSize);
        this.srate = srate;
        init();
    }

    /** Init and allocate.
     */
    protected void init() {
        mixer = new Mixer(bufferSize,3);
        delay1 = new Delay(bufferSize,srate);
        delay2 = new Delay(bufferSize,srate);
        try {
            mixer.addSource(delay1);
            mixer.setGain(0,-a);
            mixer.addSource(delay2);
            mixer.setGain(1,1);
            delay1.addSource(mixer);
        } catch(SinkIsFullException e) {
            System.out.println(this+" "+e);
        }
        long t = getTime();
        mixer.setTime(t);
        delay1.setTime(t);
        delay2.setTime(t);
    }
    
    /** Add source to Sink. Override to allow one input only and add to mixer with
        gain coefficient a and to delay 2.
        This will be called after init() so mixer will already have 2 inputs
        @param s Source to add.
        @return object representing Source in Sink (may be null).
    */
    public Object addSource(Source s) throws SinkIsFullException {
        if(getSources().length > 0) {
            throw new SinkIsFullException();
        } else {
            mixer.addSource(s);
            mixer.setGain(2,a);
            delay2.addSource(s);
            // add to the superclass. THis is for administrative reasons only,
            // the source cache is not used here.
            return super.addSource(s);
        }
    }

    /** Set delay del. (=M/srate)
        @param del delay in seconds
     */
    public void setM(float del) throws IllegalArgumentException {
        this.del = del;
        delay1.setRecursiveDelay(del);
        delay2.setRawDelay(del);
    }
    
    /** Set filter parameter a.
        @param a allpas parameter
     */
    public void setA(float a) {
        this.a = a;
        mixer.setGain(0,-a);
        mixer.setGain(2,a);
    }

    /** Compute the next buffer and store in member float[] buf.
     */
    protected void computeBuffer() {
        try {
            buf = mixer.getBuffer(getTime());
        } catch(BufferNotAvailableException e) {
            System.out.println(this+" "+e);            
        }
    }

}
